/* -*-  Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1999, 2000 Massachusetts Institute of Technology.
 *
 * This software is being provided by the copyright holders under the GNU
 * General Public License, either version 2 or, at your discretion, any later
 * version. (See the file `LICENSE' in the Linux source distribution.)
 *
 * This software is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The entire risk as
 * to the quality and performance of the program is with you. See the GNU
 * General Public License for more details.
 *
 * The name and trademarks of copyright holders may NOT be used in 
 * advertising or publicity pertaining to the software without specific, 
 * written prior permission. Title to copyright in this software and any 
 * associated documentation will at all times remain with copyright 
 * holders. See the file CMAUTHORS which should have accompanied this software 
 * for a list of all copyright holders. 
 * 
 * Particular files in this distribution may be derived from previously
 * copyrighted software. This copyright applies only to those changes made by
 * the copyright holders listed in the CMAUTHORS file.
 */
/*
 * An implementation of the Congestion Manager (CM) for Linux.
 */
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <net/cm.h>
#include <linux/wanpipe.h>
#include <net/cm_debug.h>

cm_t cm;			/* the sending host's congestion manager */
struct tq_struct cm_tq;		/* CM task queue */
/*
 * Some global default parameters for CM algorithms.
 */
int cm_shaping = 1;
int cm_aging = 0;
int cm_alive = 0;		/* for debugging */

/*
 * Called at boot time.
 */
void cm_boot()
{
	int i;

	CM_DPRINTF(CM_DEBUG_FUNCALL | CM_DEBUG_INIT,
		   "cm_debug:  entering %s (%d mflow entries)\n",
		   __FUNCTION__, CM_TABLESIZE);
	
	cm.num_mflows = 0;
	for (i = 0; i < CM_TABLESIZE; i++)
		cm.mflowtab[i] = NULL;
	printk("Initialized mflow table with %d entries... ", CM_TABLESIZE);

	cm_tq.data = (void *)1;	/* arbitrary value */
	cm_tq.sync = 0;
	cm_tq.routine = cm_wakeup;
	queue_task(&cm_tq, &tq_timer);

	CM_DPRINTF(CM_DEBUG_INIT, "cm_debug:  %s complete\n", __FUNCTION__);
}

/*
 * Periodically, or on a network interrupt, wake up and process all pending
 * macroflows.  The tasks done here relate to transmitting pending xmissions
 * across macroflows and (optionally) aging transmissions across them.
 */
int cm_wakeups = 0;
void cm_wakeup(void *data)
{
	int i;
	
	cm_wakeups++;
	queue_task(&cm_tq, &tq_timer);
	if (cm_wakeups == 10000) {
		cm_wakeups = 0;
		/* printk("cm_wakeup\n"); */
	}
	if (!cm_shaping && !cm_aging)
		return;

	/* Start with shaping (pacing). 
	   For now.  Later make a more efficient data structure.*/

	read_lock_bh(&cm.mflowtab_lock);
	/* For now, schedule at most one transmission per macroflow. */
	for (i = 0; i < CM_TABLESIZE; i++) {
		cm_macroflow_t *mflow = cm.mflowtab[i];

		if (mflow) {
			cmcc_wakeup(mflow->congctl);
			while (mflow->next) {
				mflow = mflow->next;
				cmcc_wakeup(mflow->congctl);
			}
		}
	}
	read_unlock_bh(&cm.mflowtab_lock);
}

int cm_log_data[CM_LOG_SIZE];
int cm_log_index = 0;

void cm_log(int event, u32 time, int arg1, int arg2)
{
	cm_log_data[cm_log_index] = event;
	cm_log_data[cm_log_index+1] = time;
	cm_log_data[cm_log_index+2] = arg1;
	cm_log_data[cm_log_index+3] = arg2;
	cm_log_index += 4;
#if 0
	printk("index %d event %d time %d arg1 %d arg2 %d\n", cm_log_index, event, time, arg1, arg2);
#endif
	/* assumes size of log is multiple of number of entries per call... */
	if (cm_log_index >= CM_LOG_SIZE) cm_log_index = 0;
}
